package com.xlabs.server;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.xlabs.server.DO.PMF;
import com.xlabs.server.DO.QuestionDO;
import com.xlabs.server.DO.ReplyDO;
import com.xlabs.client.DTO.QuestionDTO;
import com.xlabs.client.DTO.ReplyDTO;
import com.xlabs.client.CommentingService;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import java.util.*;


/**
 * TimedCommentServiceImpl class.
 */
public class CommentingServiceImpl extends RemoteServiceServlet implements
        CommentingService {

    /**
     * Posts comment for a video.
     *
     * @param comment        comment text.
     * @param time           time of comment.
     * @param currentVideoId video id.
     */
    public QuestionDTO[] postQuestion(String comment, int time, String currentVideoId) {

        Video.addQuestionToVideo(comment, time, currentVideoId);

        return QuestionDO.getAllQuestions();
    }

    /**
     * Gets all comments for the given video.
     *
     * @param currentVideoId video id.
     */
    public QuestionDTO[] getQuestions(String currentVideoId) {
        PersistenceManager pm = PMF.get().getPersistenceManager();
        QuestionDTO[] result;

        try {

            Video.AddVideo(currentVideoId);

            result = QuestionDO.getAllQuestions();

        }
        finally {

            pm.close();
        }

        return result;
    }

    /**
     * Posts a reply for a question.
     *
     * @param reply             reply.
     * @param questionStringKey question key.
     * @return updated comment.
     */
    public QuestionDTO[] postReply(String reply, String questionStringKey) {
        PersistenceManager pm = PMF.get().getPersistenceManager();

        try {
            Key questionKey = KeyFactory.stringToKey(questionStringKey);
            QuestionDO questionDO = pm.getObjectById(QuestionDO.class, questionKey);

            ReplyDO replyDO = new ReplyDO(reply, new Date());

            replyDO.setReplyTo(questionKey);
            replyDO.setOriginalQuestion(questionKey);

            UserService userService = UserServiceFactory.getUserService();
            replyDO.setCreatedBy(userService.getCurrentUser());

            pm.makePersistent(replyDO);

            questionDO.addReply(replyDO.getKey());
        }
        finally {
            pm.close();
        }

        return QuestionDO.getAllQuestions();
    }

    /**
     * Posts an inner reply for another reply in conversation.
     *
     * @param reply          reply.
     * @param parentReplyEncodedKey question key.
     * @param questionEncodedKey corresponding question's encoded key
     */
    public void posInnertReply(String reply, String parentReplyEncodedKey, String questionEncodedKey) {

        Key parentReplyKey = KeyFactory.stringToKey(parentReplyEncodedKey);
        Key questionKey = KeyFactory.stringToKey(questionEncodedKey);

        PersistenceManager pm = PMF.get().getPersistenceManager();

        try {
            ReplyDO parentReplyDO = pm.getObjectById(ReplyDO.class, parentReplyKey);

            ReplyDO replyDO = new ReplyDO(reply, new Date());

            replyDO.setReplyTo(parentReplyKey);
            replyDO.setOriginalQuestion(questionKey);

            UserService userService = UserServiceFactory.getUserService();
            replyDO.setCreatedBy(userService.getCurrentUser());

            pm.makePersistent(replyDO);

            parentReplyDO.addReply(replyDO.getKey());
        }
        finally {
            pm.close();
        }
    }

    /**
     * Gets the conversation related to the question given.
     *
     * @param questionEncodedStringKey question's key.
     * @return conversation.
     */
    public List<ReplyDTO> getConversation(String questionEncodedStringKey) {
        PersistenceManager pm = PMF.get().getPersistenceManager();

        Key questionKey = KeyFactory.stringToKey(questionEncodedStringKey);

        List<ReplyDO> orderedReplies = new ArrayList<ReplyDO>();

        try {
            Query query = pm.newQuery(ReplyDO.class);
            query.setFilter("originalQuestion == questionKey");
            query.setOrdering("createdOn asc");
            query.declareParameters("String questionKey");

            try {
                orderedReplies = (List<ReplyDO>) query.execute(questionKey);
            }
            finally {
                query.closeAll();
            }

            return this.createConversationTree(orderedReplies);
        }
        finally {
            pm.close();
        }
    }

    /**
     * Orders the replies and return them.
     *
     * @param orderedReplies List of unordered replies.
     * @return Ordered replies.
     */
    private List<ReplyDTO> createConversationTree(List<ReplyDO> orderedReplies) {
        List<ReplyDTO> result = new ArrayList<ReplyDTO>(orderedReplies.size());

        Map<Key, ReplyDO> repliesKeyToReplyDOMap = new HashMap<Key, ReplyDO>(orderedReplies.size() + 1);
        Map<Key, ReplyDTO> repliesKeyToReplyDTOMap = new HashMap<Key, ReplyDTO>(orderedReplies.size() + 1);
        
        for (ReplyDO reply : orderedReplies) {
            repliesKeyToReplyDOMap.put(reply.getKey(), reply);
        }

        for (ReplyDO reply : orderedReplies)
        {
            ReplyDTO replyDTO = reply.toReplyDTO();
            
            repliesKeyToReplyDTOMap.put(reply.getKey(), replyDTO);

            Key replyToKey = reply.getReplyTo();
            
            replyDTO.setParent(repliesKeyToReplyDTOMap.get(replyToKey));

            result.add(replyDTO);
        }

        return result;
    }
}